//  BinaryReal.java
//
//  Author:
//       Antonio J. Nebro <antonio@lcc.uma.es>
//       Juan J. Durillo <durillo@lcc.uma.es>
// 
//  Copyright (c) 2011 Antonio J. Nebro, Juan J. Durillo
//
//  This program is free software: you can redistribute it and/or modify
//  it under the terms of the GNU Lesser General Public License as published by
//  the Free Software Foundation, either version 3 of the License, or
//  (at your option) any later version.
//
//  This program is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU Lesser General Public License for more details.
// 
//  You should have received a copy of the GNU Lesser General Public License
//  along with this program.  If not, see <http://www.gnu.org/licenses/>.

package etmo.encodings.variable;

import etmo.core.Variable;
import etmo.util.JMException;

import java.util.BitSet;

/**
 * This class extends the Binary class to represent a Real encodings.variable
 * encoded by a binary string
 */
public class BinaryReal extends Binary {

	/**
	 * Defines the default number of bits used for binary coded variables.
	 */
	public static final int DEFAULT_PRECISION = 30;

	/**
	 * Stores the real value of the encodings.variable
	 */
	private double value_;

	/**
	 * Stores the lower limit for the encodings.variable
	 */
	private double lowerBound_;

	/**
	 * Stores the upper limit for the encodings.variable
	 */
	private double upperBound_;

	/**
	 * Constructor.
	 */
	public BinaryReal() {
		super();
	} // BinaryReal

	/**
	 * Constructor
	 * 
	 * @param numberOfBits
	 *            Length of the binary string.
	 * @param lowerBound
	 *            The lower limit for the encodings.variable
	 * @param upperBound
	 *            The upper limit for the encodings.variable.
	 */
	public BinaryReal(int numberOfBits, double lowerBound, double upperBound) {
		super(numberOfBits);
		lowerBound_ = lowerBound;
		upperBound_ = upperBound;

		decode();
	} // BinaryReal

	/**
	 * @param bits
	 *            BitSet
	 * @param nbBits
	 *            Number of bits
	 * @param lowerBound
	 *            Lower bound
	 * @param upperBound
	 *            Upper bound
	 */
	public BinaryReal(BitSet bits, int nbBits, double lowerBound, double upperBound) {
		super(nbBits);
		bits_ = bits;
		lowerBound_ = lowerBound;
		upperBound_ = upperBound;
		decode();
	} // BinaryReal

	/**
	 * Copy constructor
	 * 
	 * @param variable
	 *            The encodings.variable to copy
	 */
	public BinaryReal(BinaryReal variable) {
		super(variable);

		lowerBound_ = variable.lowerBound_;
		upperBound_ = variable.upperBound_;
		/*
		 * numberOfBits_ = encodings.variable.numberOfBits_;
		 * 
		 * bits_ = new BitSet(numberOfBits_); for (int i = 0; i < numberOfBits_;
		 * i++) bits_.set(i,encodings.variable.bits_.get(i));
		 */
		value_ = variable.value_;
	} // BinaryReal

	/**
	 * Decodes the real value encoded in the binary string represented by the
	 * <code>BinaryReal</code> object. The decoded value is stores in the
	 * <code>value_</code> field and can be accessed by the method
	 * <code>getValue</code>.
	 */
	public void decode() {
		double value = 0.0;
		for (int i = 0; i < numberOfBits_; i++) {
			if (bits_.get(i)) {
				value += Math.pow(2.0, i);
			}
		}

		value_ = value * (upperBound_ - lowerBound_) / (Math.pow(2.0, numberOfBits_) - 1.0);
		value_ += lowerBound_;
	} // decode

	/**
	 * Returns the double value of the encodings.variable.
	 * 
	 * @return the double value.
	 */
	public double getValue() {
		return value_;
	} // getValue

	/**
	 * This implementation is efficient for binary string of length up to 24
	 * bits, and for positive intervals.
	 * 
	 * @see etmo.core.Variable#setValue(double)
	 * 
	 *      Contributor: jl hippolyte
	 */
	@Override
	public void setValue(double value) throws JMException {
		if (numberOfBits_ <= 24 && lowerBound_ >= 0) {
			BitSet bitSet;
			if (value <= lowerBound_) {
				bitSet = new BitSet(numberOfBits_);
				bitSet.clear();
			} else if (value >= upperBound_) {
				bitSet = new BitSet(numberOfBits_);
				bitSet.set(0, numberOfBits_);
			} else {
				bitSet = new BitSet(numberOfBits_);
				bitSet.clear();
				// value is the integerToCode-th possible value, what is
				// integerToCode?
				int integerToCode = 0;
				double tmp = lowerBound_;
				double path = (upperBound_ - lowerBound_) / (Math.pow(2.0, numberOfBits_) - 1);
				while (tmp < value) {
					tmp += path;
					integerToCode++;
				}
				int remain = integerToCode;
				for (int i = numberOfBits_ - 1; i >= 0; i--) {
					// System.out.println("i= " + i);
					int ithPowerOf2 = (int) Math.pow(2, i);

					if (ithPowerOf2 <= remain) {
						// System.out
						// .println(ithPowerOf2thValue + " <= " + remain);
						bitSet.set(i);
						remain -= ithPowerOf2;
					} else {
						bitSet.clear(i);
					}
				}
			}
			this.bits_ = bitSet;
			this.decode();

		} else {
			if (lowerBound_ < 0)
				throw new JMException("Unsupported lowerbound: " + lowerBound_ + " > 0");
			if (numberOfBits_ >= 24)
				throw new JMException("Unsupported bit string length" + numberOfBits_ + " is > 24 bits");
		}
	}// setValue

	/**
	 * Creates an exact copy of a <code>BinaryReal</code> object.
	 * 
	 * @return The copy of the object
	 */
	public Variable deepCopy() {
		return new BinaryReal(this);
	} // deepCopy

	/**
	 * Returns the lower bound of the encodings.variable.
	 * 
	 * @return the lower bound.
	 */
	public double getLowerBound() {
		return lowerBound_;
	} // getLowerBound

	/**
	 * Returns the upper bound of the encodings.variable.
	 * 
	 * @return the upper bound.
	 */
	public double getUpperBound() {
		return upperBound_;
	} // getUpperBound

	/**
	 * Sets the lower bound of the encodings.variable.
	 * 
	 * @param lowerBound
	 *            the lower bound.
	 */
	public void setLowerBound(double lowerBound) {
		lowerBound_ = lowerBound;
	} // setLowerBound

	/**
	 * Sets the upper bound of the encodings.variable.
	 * 
	 * @param upperBound
	 *            the upper bound.
	 */
	public void setUpperBound(double upperBound) {
		upperBound_ = upperBound;
	} // setUpperBound

	/**
	 * Returns a string representing the object.
	 * 
	 * @return the string.
	 */
	@Override
	public String toString() {
		return value_ + "";
	} // toString
} // BinaryReal
